
#include "ellipse.h"
/*
 * TEllipse
 */
 
 /**
 Initialises the values of the ellipse so that it conforms to a rectangle entered as a parameter.
 @param aRect the rectangle within which the ellipse is drawn
 */
void TEllipse::Construct(const TRect& aRect)
	{
	TInt width=aRect.Width();
	TInt height=aRect.Height();
	iA=(width-1)>>1;
	iB=(height-1)>>1;
	iXAdj=(width+1)&1;
	iYAdj=(height+1)&1;
	iOffset=aRect.iTl;
	iX=0;
	iY=iB;
	iASquared=iA*iA;
	iBSquared=iB*iB;
	iASquBSqu=iASquared*iBSquared;
	iD1=iBSquared-iASquared*iB+(iASquared>>1);
	if(width<=0 || height<=0)
		iStatus=EComplete;
	else if(width<=2 || height<=2)
		iStatus=ELine;
	else if(iA+iXAdj==0 || iB+iYAdj==0)
		iStatus=EComplete;
	else
		iStatus=EInitialised;
	}

/**
Does the next stage in producing an ellipse by taking four points (the corners of 
the rectangle the ellipse should fill) as parameters. Updates TEllipse status 
accordingly and calls <code>Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight)</code>.
@param aTopLeft Top left corner of rectangle 
@param aTopRight Top right corner of rectangle
@param aBottomLeft Bottom left corner of rectangle
@param aBottomRight Bottom right corner of rectangle
@return TBool ETrue if step completed successfully.
*/
TBool TEllipse::SingleStep(TPoint& aTopLeft,TPoint& aTopRight,
		TPoint& aBottomLeft,TPoint& aBottomRight)
	{
		TBool ret=EFalse;
		if(iStatus==EFirstSector)
		{
			if(iD1<0)
				iD1+=iBSquared*((iX<<1)+3);
			else if(iY>0)
			{
				iD1+=iBSquared*((iX<<1)+3)+iASquared*(2-(iY<<1));
				iY--;
			}
			iX++;
			ret=Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight);
			if(iStatus==EComplete && iX<iA)
			{
				iStatus=EFirstSector;
				return(EFalse);
			}
			if(iASquared*iY<=iBSquared*(iX+1) && ret==EFalse)
			{
				iStatus=ESecondSector;
				iD2=-iASquBSqu+iBSquared*iX*iX+iASquared*(iY-1)*(iY-1);
			}
			return(ret);
		}
		if(iStatus==ESecondSector)
		{
			if(iD2<0)
			{
				iD2+=iBSquared*((iX<<1)+2)+iASquared*(3-(iY<<1));
				iX++;
			}
			else
				iD2+=iASquared*(3-(iY<<1));
			iY--;
			return(Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight));
		}
		if(iStatus==ELine)
		{
			ret=Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight);
			if(iA==0)
				iY--;
			else
			{
				iX++;
				if(iX>iA+iXAdj) ret=ETrue;
				else
				{
					iStatus=ELine;
					ret=EFalse;
				}
			}
			return(ret);
		}
		if(iStatus==EInitialised)
		{
			iStatus=EFirstSector;
			return(Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight));
		}
		Output(aTopLeft,aTopRight,aBottomLeft,aBottomRight);
		return(ETrue);
	}

	/**
	Sets the absolute points that define the ellipse as calculated using its iOffset 
	from the origin and using the half width and half height of the rectangle iA and iB.
	@param aTopLeft The absolute (x,y) position for the top left point.
	@param aTopRight The absolute (x,y) position for the top right point.
	@param aBottomLeft The absolute (x,y) position for the bottom left point.
	@param aBottomRight The absolute (x,y) position for the bottom right point.
	@return TBool ETrue if a valid rectangle is produced, else EFalse. Also sets
	iStatus to EComplete.
	*/
TBool TEllipse::Output(TPoint& aTopLeft,TPoint& aTopRight,
		TPoint& aBottomLeft,TPoint& aBottomRight)
	{
		TInt lx=iA-iX+iOffset.iX;
		TInt ty=iB-iY+iOffset.iY;
		TInt rx=iA+iX+iXAdj+iOffset.iX;
		TInt by=iB+iY+iYAdj+iOffset.iY;
		aTopLeft.SetXY(lx,ty);
		aTopRight.SetXY(rx,ty);
		aBottomLeft.SetXY(lx,by);
		aBottomRight.SetXY(rx,by);
		if(iY<=0)
		{
			iStatus=EComplete;
			if(iYAdj==0 || ty>by)
				return(ETrue);
		}
		return(EFalse);
	}

/**
By analysing the current state of the ellipse the process is taken to the next appropriate step.
If iStatus = EInitialised only one step will be taken, if the ellipse is already semi constructed then 
it will be taken to completion. Takes in four point parameters that defines the rectangle in order to pass to 
SingleStep(aTopLeft,aTopRight,aBottomLeft,aBottomRight).
@param aTopLeft Top left corner of rectangle 
@param aTopRight Top right corner of rectangle
@param aBottomLeft Bottom left corner of rectangle
@param aBottomRight Bottom right corner of rectangle
@return TBool ETrue if a valid rectangle is produced, else EFalse.
*/
TBool TEllipse::NextStep(TPoint& aTopLeft,TPoint& aTopRight,
								  TPoint& aBottomLeft,TPoint& aBottomRight)
	{
	if(iStatus==EInitialised)
		return(SingleStep(aTopLeft,aTopRight,aBottomLeft,aBottomRight));
	TInt prevlev=iY;
	TBool ret;
	do
		ret=SingleStep(aTopLeft,aTopRight,aBottomLeft,aBottomRight);
	while(prevlev==iY && ret==EFalse);
	return(ret);
	}

/**
Constructs an ellipse from the rectangle which it is given and assesses the 
points position with regard to the ellipse and where they intersect. 
@param aRect The rectangle within which the ellipse is drawn.
@param aPoint A point to compare with the ellipse to determine if intersection occurs. 
@return TPoint The point is set to the corner which the intersection is nearest to.
*/
TPoint TEllipse::Intersection(const TRect& aRect,const TPoint& aPoint)
	{
	Construct(aRect);					//constructs the rect (an elipse object)
	TPoint centre=aRect.Center();		//centre of ellipse
	TPoint ptcpy(aPoint);				
	ptcpy-=iOffset;						//ptcpy = aPoint - iOffset - TPoint(iA,iB)	//radius from centre of ellipse		
	ptcpy-=TPoint(iA,iB);				
	TPoint pt[4],opt[4];			
	TInt mpt[4],ompt[4];
	TInt count=0;
	for(;count<4;count++)
		ompt[count]=KMaxTInt;			//fills ompt 1->4 with KMaxTInt
	while(SingleStep(pt[0],pt[1],pt[2],pt[3])==EFalse) 	//creates a complete ellipse with pts as rect
		for(count=0;count<4;count++)
			{
			mpt[count]=Abs((pt[count].iY-iOffset.iY-iB)*(ptcpy.iX)-(ptcpy.iY)*(pt[count].iX-iOffset.iX-iA));
			if(mpt[count]<ompt[count]) //use the larger number set.
				{
				ompt[count]=mpt[count];
				opt[count]=pt[count];
				}						
			}
	if(pt[0].iY==pt[2].iY)	//if it is horizontal
		for(count=0;count<4;count++)
			{
			mpt[count]=Abs((pt[count].iY-iOffset.iY-iB)*(ptcpy.iX)-(ptcpy.iY)*(pt[count].iX-iOffset.iX-iA));
			if(mpt[count]<ompt[count]) //use the larger number set.
				{
				ompt[count]=mpt[count];
				opt[count]=pt[count];
				}
			}
	if(ptcpy.iX<0 && ptcpy.iY<0)	//if point is further left and higher than centre of rect
		return(opt[0]);
	if(ptcpy.iY<0)			//if point is higher than centre of rect
		return(opt[1]);
	if(ptcpy.iX<0)			//if point is further left than centre of rect
		return(opt[2]);
	if(aPoint.iX<centre.iX && aPoint.iY<centre.iY)	//if point is further left and higher than centre of rect
		return(opt[0]);
	if(aPoint.iY<centre.iY)	//if point is higher than centre of rect
		return(opt[1]);
	if(aPoint.iX<centre.iX)	//if point is further left than centre of rect
		return(opt[2]);
	return(opt[3]);			//else 
	}